#!/usr/bin/env python
# encoding: utf-8
"""
Provide a programmatic interface to the Oracle database at
http://gatherer.wizards.com/

Copyright 2007 Adam Blinkinsop <blinks@acm.org>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

  Test usage (on pre-downloaded search) below:

>>> fixtures = open('test/goblin_warrior.html', 'r')
>>> results = parseResults(fixtures, 'Spoiler')

  The results are a list of dictionaries.
  Each dictionary uses the column headers for keys.
  For example:

>>> print results[0]['Name']
Adder-Staff Boggart

  All mana images (and the "tap" symbol) are converted into their
  comprehensive rulebook form.  For example:
  
>>> print results[1]['Cost']
{5}{B}

  Lines are split by line feed, not carriage return.

>>> print results[1]['Rules Text'].split('\\n')[0]
Bog Hoodlums can't block.

  Other various examples:

>>> print results[3]['P'], results[3]['T']
1 2
>>> print results[2]['Printings']
Lorwyn (Rare)
>>> print results[4]['Type'].split(' - ')[0]
Creature

>>> fixtures = open('test/angelic_urza.html', 'r')
>>> results = parseResults(fixtures, 'Summary')
>>> print results[0]['Printings']
Tenth Edition (Rare)
Urza's Saga (Rare)
"""

import sys
import os
import re
import copy
from urllib import urlopen, urlencode
from BeautifulSoup import BeautifulSoup

GATHERER_URL = 'http://gatherer.wizards.com/'

def render(tag):
  """Render Oracle data to plain text."""
  data = tag.renderContents()
  data = re.sub(r'\r', '\n', data)
  
  def fromAlt(m):
    alt = m.group(1)
    if alt.endswith(' Mana'):
      w = alt.split()[0]
      if w == 'White':   return '{W}'
      elif w == 'Blue':  return '{U}'
      elif w == 'Black': return '{B}'
      elif w == 'Red':   return '{R}'
      elif w == 'Green': return '{G}'
      elif w == 'Snow':  return '{S}'
      elif w == 'X':     return '{X}'
      elif re.match('(\w/\w)', w): 
        return '{%s}' % w
      else: return '{%i}' % int(w)
    elif alt == 'Tap':  return '{T}'
    else: return alt
  data = re.sub(r'(?m)<img.*?alt="(.*?)".*?/>', fromAlt, data)
  # Tags to ignore.
  data = re.sub(r'(?m)</?(font|[abi]).*?>', r'', data)
  # Tags to replace.
  data = re.sub(r'&nbsp;', ' ', data)
  return data.strip()


def select(**args):
  """Query the Oracle for a list of cards."""
  
  # Set the default query arguments.
  args.setdefault('q', '')
  args.setdefault('group', '')
  args.setdefault('color', 'All')
  args.setdefault('card_type', 'All Types')
  args.setdefault('output', 'Summary')
  args.setdefault('sort_by', 'Name')
  params = {
    'term': args['q'],
    'Field_Name': 'on', 'Field_Rules': 'on', 'Field_Type': 'on',
    'setfilter': args['group'],
    'colorfilter': args['color'],
    'typefilter': args['card_type'],
    'output': args['output'], 'sort': args['sort_by'],
  }
  f = urlopen('%s?%s' % (GATHERER_URL, urlencode(params)))
  return parseResults(f, params['output'])


def parseResults(f, format='Summary'):
  """Parse the results of a query from a file."""

  def _fromTable(tag, th='th'): 
    trs = tag.findAll('tr')
    keys = [render(td) for td in trs.pop(0).findAll(th)]
    trs = [[render(td) for td in tr.findAll('td')] for tr in trs]
    return [dict(zip(keys, tds)) for tds in trs]
  
  # Massage the markup to fix Wizards' broken code.
  massage = copy.copy(BeautifulSoup.MARKUP_MASSAGE)
  massage.extend([
	  # Fix broken alt text (alt='Urza's Legacy')
	  (re.compile(r"'(Urza's.+?)'"), lambda m: '"%s"' % m.group(1)),
	])
  soup = BeautifulSoup(f, markupMassage=massage)
	
  if format == 'Summary' or format == 'Spoiler':
    table = soup.find('table', attrs={'id': '_gridResults'})
    return _fromTable(table)
  elif format == 'Text Checklist':
    table = soup.find('table', attrs={'class': 'TextResultsTable'})
    return _fromTable(table, 'td')
  else:
    raise NotImplementedError("Can't parse this output type: %s" % output)

def _test():
  import doctest
  doctest.testmod()

if __name__ == '__main__':
  _test()
